import Base from '../bagua/base.js'
import Zhi from '../bagua/zhi.js'
import Ganzhi from '../bagua/ganzhi.js'
import Bagua from '../bagua/bagua.js'
import Mountain from '../bagua/mountain.js'
import TaiyiGround from '../bagua/taiyiGround.js'

import CMDate from '../date/cmDate.js'
import TimeGod from '../relation/timeGod.js'

import dictionary from '../bagua/dictionary.js'

// 九宫对应的八卦(后天)
const palacesBaguaList = ['巽', '离', '坤', '震', null, '兑', '艮', '坎', '乾']
// 九宫后天宫位数
const palacesAfterNumList = [4, 9, 2, 3, 5, 7, 8, 1, 6]
// 九宫先天宫位数
const palacesBeforeNumList = [5, 3, 8, 4, null, 2, 7, 6, 1]
// 宫位指针顺序表，顺时针/逆时针
const palacesClockPointers = [
  [1, 3], [2, 0], [5, 1],
  [0, 6], [5, 1], [8, 2],
  [3, 7], [6, 8], [7, 5]
]

// 太乙宫位数
const palacesTaiyiNumList = [9, 2, 7, 4, 5, 6, 3, 8, 1]


// 各种局的基本类
class BaseStage extends Base {
  constructor(options = {}) {
    super(options)
    
    this.initBaseStage()
  }
  
  // 基础初始化
  initBaseStage () {
    this.types.push('BaseStage')
    
    // 时间信息具体化
    if (!this.options.date) {
      // 这里必须做一层判断,因为CMDate就算传入undefined也会用当下时间有效初始化
      this.enabled = false
      return
    }
    // 既然要起局，就必须创建完整的日期
    this.date = new CMDate(this.options.date, true)

    if (!this.date || !this.date.enabled) {
      // 若没有成功生成日期，直接跳出
      this.enabled = false
      return
    }
    // 有了日期后，创建神煞对象
    this.timeGod = new TimeGod()
    
    // 排局完成前就可以根据时间对象寻找的神煞
    this.initBeforeTimeGod()
    
    // 标题
    this.title = typeof this.options.title === 'string' && this.options.title !== '' ? this.options.title : '无标题'
  }
  
  /* @section 神煞确定的方法 */
  // 排局完成前就可以根据时间对象寻找的神煞
  initBeforeTimeGod () {    
    // 计算太阴
    this.timeGod.initTaiyin(this.date)
    
    // 计算四柱驿马
    this.timeGod.initTimeHorses(this.date)
    // 计算四柱天乙贵人
    this.timeGod.initTimeGuiGods(this.date)
    
    // 计算四柱空亡
    this.timeGod.initTimeEmpty(this.date)
    // 计算旺相休囚死
    this.timeGod.initSeasonPower(this.date)
  }
  
  // 设置排局完成后才能排布的神煞
  initAfterTimeGod () {}
  
  /* @section 初始化结构的方法 */
  // 初始化九宫八卦
  initPalaces () {
    // 初始化宫位指针信息
    BaseStage.setPalacesPointers()
    
    let palaces = []
    const len = palacesBaguaList.length
    // 创建九宫八卦
    for (let i = 0; i < len; i++) {
      let palace = {
        order: i,
        // 为了向v-for循环妥协，定义的一系列id
        baseId: 'palace-' + i,
        outerId: 'outer-' + i,
        godId: 'palaceGod-' + i,
        // id定义结束
        taiyiNum: palacesTaiyiNumList[i],
        afterNum: palacesAfterNumList[i],
        beforeNum: palacesBeforeNumList[i],
        pointer: {
          // 先后天宫位数向前/向后
          afterPrev: BaseStage.palacesPointers[i].afterPrev,
          afterNext: BaseStage.palacesPointers[i].afterNext,
          beforePrev: BaseStage.palacesPointers[i].beforePrev,
          beforeNext: BaseStage.palacesPointers[i].beforeNext,
          // 太乙宫位数向前/向后
          taiyiPrev: BaseStage.palacesPointers[i].taiyiPrev,
          taiyiNext: BaseStage.palacesPointers[i].taiyiNext,
          // 顺时针、逆时针的顺序指针
          go: palacesClockPointers[i][0],
          anti: palacesClockPointers[i][1]
        },
        // 元素集合
        ele: {}
      }
      
      // 八卦加入九宫
      const bagua = palacesBaguaList[i]
      if (bagua) {
        palace.description = bagua + '宫'
        palace.ele.bagua = new Bagua(bagua, {
          description: bagua + dictionary.BAGUA
        })
      }
      else {
        palace.description = '中宫'
      }
      
      palaces.push(palace)
    }
    return palaces
  }
  
  // 初始化地支十二神的结构
  initZhiPalaces () {
    let palaces = []
    const len = Zhi.zhiList.length
    // 创建地支十二神
    for (let i = 0; i < len; i++) {
      let palace = {
        order: i,
        // 为了向v-for循环妥协，定义的一系列id
        baseId: 'zhi-' + i,
        godId: 'zhiGod-' + i,
        // id定义结束
        
        pointer: {
          // 顺时针、逆时针的顺序指针
          go: (i + 1) % len,
          anti: (i + len - 1) % len
        },
        // 元素集合
        ele: {}
      }
      
      palace.description = Zhi.zhiList[i] + '宫'
      palace.ele.zhi = new Zhi(i, {
        description: palace.description + dictionary.GUOUND_ZHI
      })
      palaces.push(palace)
    }
    return palaces
  }
  
  // 初始化二十四山向的结构
  initMountainPalaces () {
    let palaces = []
    const len = Mountain.mountainList.length
    for (let i = 0; i < len; i++) {
      let palace = {
        order: i,
        // 为了向v-for循环妥协，定义的一系列id
        baseId: 'mountain-' + i,
        godId: 'mountainGod-' + i,
        // id定义结束
        
        pointer: {
          // 顺时针、逆时针的顺序指针
          go: (i + 1) % len,
          anti: (i + len - 1) % len
        },
        // 元素集合
        ele: {}
      }
      
      palace.description = Mountain.mountainList[i] + '位'
      palace.ele.mountain = new Mountain(i, {
        description: palace.description + dictionary.MOUNTAIN + dictionary.DIRECTION
      })
      palaces.push(palace)
    }
    return palaces
  }
  
  // 初始化太乙十六神的结构
  initTaiyiPalaces () {
    let palaces = []
    const len = TaiyiGround.taiyiGroundList.length
    for (let i = 0; i < len; i++) {
      let palace = {
        order: i,
        // 为了向v-for循环妥协，定义的一系列id
        baseId: 'taiyiGround-' + i,
        // id定义结束
        
        pointer: {
          // 顺时针、逆时针的顺序指针
          go: (i + 1) % len,
          anti: (i + len - 1) % len
        },
        // 元素集合
        ele: {}
      }
      
      palace.description = TaiyiGround.taiyiGroundSimList[i] + '宫'
      palace.ele.taiyiGround = new TaiyiGround(i, {
        description: palace.description + dictionary.TAIYI_GROUND
      })
      palace.ele.taiyiSky = []
      palaces.push(palace)
    }
    return palaces
  }
  
  // 联立各种宫
  connect () {
    // 联立九宫和地支十二神
    this.connectZhiPalaces()
    // 联立九宫和二十四山向
    // this.connectMountainPalaces()
    
    // 联立其他宫
  }
  
  // 联立九宫和地支
  connectZhiPalaces () {
    if (this.palaces && this.zhiPalaces) {
      // 联立九宫和地支十二神
      const len = Zhi.zhiList.length
      // 从坎宫开始
      let curPalace = this.palaces[7]
      for (let i = 0; i < len; i++) {
        if (!curPalace.pointer.zhi) {
          curPalace.pointer.zhi = []
        }
        // 指针互相指定
        curPalace.pointer.zhi.push(i)
        this.zhiPalaces[i].pointer.palace = curPalace.order
        if (i % 3 === 0 || (i + 1) % 3 === 0) {
          // 顺时针换到下一宫
          curPalace = this.palaces[curPalace.pointer.go]
        }
      }
    }
  }
  
  // 联立九宫和太乙十六神
  connectTaiyiPalaces () {
    if (this.palaces && this.taiyiPalaces) {
      const len = TaiyiGround.taiyiGroundList.length
      // 从坎宫开始
      let curPalace = this.palaces[7]
      for (let i = 0; i < len; i++) {
        if (!curPalace.pointer.taiyi) {
          curPalace.pointer.taiyi = []
        }
        // 指针互相指定
        curPalace.pointer.taiyi.push(i)
        this.taiyiPalaces[i].pointer.palace = curPalace.order
        this.taiyiPalaces[i].taiyiNum = curPalace.taiyiNum
        if (i % 2 !== 0) {
          // 顺时针换到下一宫
          curPalace = this.palaces[curPalace.pointer.go]
        }
      }
    }
  }
  
  // 联立九宫和山向
  connectMountainPalaces () {
    
  }
  
  /* @section 遍历方法 */
  /*
    遍历方法
    key 遍历对象组的key
    start 宫位索引起点
    offset 正为顺，负数为逆数，至少为1或者-1
    
    extra 每次遍历对对象进行操作的函数
    extra 返回值说明
    返回Object，则赋予对应的对象
    返回true，直接跳过，索引不增加
    返回false，强制中断遍历
    不返回，或者返回undefined，不做任何动作，继续遍历
    
    next 如何寻找下一个对象的函数，必须返回下一个对象，否则遍历将无效
  */
  trave (key = 'palaces', start = 0, offset = 1, extra = null, next = null) {
    const list = this[key]
    if (!list) {
      // 找不到遍历对象
      return
    }
    
    let cur = list[start]
    // 强制结束标志
    let endSign = false
    // 遍历的距离
    const len = offset < 0 ? (-1) * offset : offset
    for (let i = 0; i < len && !endSign; i++) {
      // 对象处理
      if (typeof extra === 'function') {
        const result = extra(cur, i, list)
        if (result === false) {
          // 强制中断整个遍历
          endSign = true
        }
        else if (result === true) {
          // 不减少遍历次数，跳过
          i--
        }
        else if (typeof result === 'object' && result) {
          // 修改当前对象
          cur = result
        }
      }
      
      // 继续前进
      if (typeof next === 'function') {
        const result = next(cur, i, list)
        if (typeof result === 'object' && result) {
          // 修改当前对象
          cur = result
        }
      }
    }
  }
 
  // 按照后天宫位数顺序遍历宫位
  travePalaceByAfterNum (start = 0, offset = 1, extra = null) {
    this.trave('palaces', start, offset, extra, (item, index, list) => {
      // 继续前进的方法
      if (offset < 0) {
        return list[item.pointer.afterPrev]
      }
      return list[item.pointer.afterNext]
    })
  }
  
  // 按照先天宫位数顺序遍历宫位
  travePalaceByBeforeNum (start = 0, offset = 1, extra = null) {
    this.trave('palaces', start, offset, extra, (item, index, list) => {
      // 继续前进的方法
      if (offset < 0) {
        return list[item.pointer.beforePrev]
      }
      return list[item.pointer.beforeNext]
    })
  }
  
  // 按照太乙宫位数遍历宫位
  travePalaceByTaiyiNum (start = 0, offset = 1, extra = null) {
    this.trave('palaces', start, offset, extra, (item, index, list) => {
      // 继续前进的方法
      if (offset < 0) {
        return list[item.pointer.taiyiPrev]
      }
      return list[item.pointer.taiyiNext]
    })
  }
  
  // 按照时针顺序遍历宫位
  travePalaceByClock (start = 0, offset = 1, extra = null) {
    this.trave('palaces', start, offset, extra, (item, index, list) => {
      // 继续前进的方法
      if (offset < 0) {
        return list[item.pointer.anti]
      }
      return list[item.pointer.go]
    })
  }
  
  /*
    按照时针顺序遍历地支十二神
    start 宫位索引起点
    offset 正为顺时针，负数为逆时针
    extra 每次遍历对对象进行操作的函数
  */
  traveZhiByClock (start = 0, offset = 1, extra = null) {
    this.trave('zhiPalaces', start, offset, extra, (item, index, list) => {
      // 继续前进的方法
      if (offset < 0) {
        return list[item.pointer.anti]
      }
      return list[item.pointer.go]
    })
  }
  
  /*
    按照顺时针顺序遍历二十四山向
  */
  traveMountainByClock (start = 0, offset = 1, extra = null) {
    this.trave('mountainPalaces', start, offset, extra, (item, index, list) => {
      // 继续前进的方法
      if (offset < 0) {
        return list[item.pointer.anti]
      }
      return list[item.pointer.go]
    })
  }
  
  /* @section 静态方法 */
  // 获取宫位指针表
  static setPalacesPointers () {
    if (BaseStage.palacesPointers) {
      // 若已有，则不必创建
      return
    }
    
    let result = []
    const len = palacesBaguaList.length
    for (let i = 0; i < len; i++) {
      let obj = {}
      const afterNum = palacesAfterNumList[i]
      const beforeNum = palacesBeforeNumList[i]
      const taiyiNum = palacesTaiyiNumList[i]
      if (afterNum) {
        // 后天序数向前/向后
        obj.afterPrev = palacesAfterNumList.indexOf((afterNum + len - 2) % len + 1)
        obj.afterNext = palacesAfterNumList.indexOf(afterNum % len + 1)
      }
      else {
        obj.afterPrev = null
        obj.afterNext = null
      }
      if (beforeNum) {
        // 先天序数向前/向后
        const beforeLen = len - 1
        obj.beforePrev = palacesBeforeNumList.indexOf((beforeNum + beforeLen - 2) % beforeLen + 1)
        obj.beforeNext = palacesBeforeNumList.indexOf(beforeNum % beforeLen + 1)
      }
      else {
        obj.beforePrev = null
        obj.beforeNext = null
      }
      if (taiyiNum) {
        // 太乙序数向前/向后
        obj.taiyiPrev = palacesTaiyiNumList.indexOf((taiyiNum + len - 2) % len + 1)
        obj.taiyiNext = palacesTaiyiNumList.indexOf(taiyiNum % len + 1)
      }
      else {
        obj.taiyiPrev = null
        obj.taiyiNext = null
      }
      // 确定顺时针/逆时针的列表
      obj.go = palacesClockPointers[i][0]
      obj.anti = palacesClockPointers[i][1]
      result.push(obj)
    }
    BaseStage.palacesPointers = result
  }
  
  // 辅助排外圈神煞，获取外圈神煞专属的键名
  static getOuterKey (god, extra) {
    if (typeof extra !== 'string') { return god }
    return [god].concat(extra.split(' ')).join('-')
  }
  
  /*
    辅助排外圈神煞，根据时间关键字，返回对应的干支对象
  */
  static getGanzhiByTimeKey (stage, type) {
    const timeKeys = ['year', 'month', 'day', 'hour', 'min']
    const timeIndex = timeKeys.indexOf(type)
    if (timeIndex === -1 && type === 'key' && stage.keyGanzhi) {
      // 返回keyGanzhi 即关键定局干支 的位置
      return stage.keyGanzhi
    }
    else if (timeIndex >= 0 && timeIndex < timeKeys.length) {
      // 按照对应的柱起建
      return stage.date.ganzhi[timeIndex]
    }
    
    return null
  }
  
  /*
    辅助排外圈神煞，根据干支类型、局本身的类型，获取地支起始索引
    return 0 - 11 返回了地支索引
    return -1 无法获取，返回失败
  */
  static getZhiStartByTimeKey (stage, type) {
    const ganzhi = BaseStage.getGanzhiByTimeKey(stage, type)
    if (ganzhi) {
      return ganzhi.zhi.index
    }
    return -1
  }
}

Object.assign(BaseStage, {
  dictionary,
  // 宫位后天八卦名
  palacesBaguaList,
  // 宫位后天八卦数
  palacesAfterNumList,
  // 太乙宫位数
  palacesTaiyiNumList
})

export default BaseStage